﻿<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8" />
  <title></title>
  <script src="js/jquery.min.js"></script>
  <link type="text/css" href="./css/style.css" rel="stylesheet" />
  <style>
    html,
    body {
      width: 100%;
      height: 100%;
      margin: 0;
      padding: 0;
      overflow: hidden;
    }

    .container {
      width: 100%;
      height: 100%;
      margin: 0;
      padding: 0;
      background-color: #000000;
    }

    .love_text {
      font-size: 48px;
      color: #fff;
      position: absolute;
      left: 50%;
      top: 70%;
      font-weight: 999;
      transform: translate(-50%, -50%);
      background-image: -webkit-linear-gradient(bottom, red, #ff5f60, #f0c41b);
      -webkit-background-clip: text;
      -webkit-text-fill-color: transparent;

    }
  </style>
</head>

<body>
  <audio autoplay="autopaly">
    <source src="renxi.mp3" type="audio/mp3" />
  </audio>
  <div id="jsi-cherry-container" class="container">

    <div class="box">
      <ul class="minbox">
        <li></li>
        <li></li>
        <li></li>
        <li></li>
        <li></li>
        <li></li>
      </ul>
      <ol class="maxbox">
        <li></li>
        <li></li>
        <li></li>
        <li></li>
        <li></li>
        <li></li>
      </ol>
    </div>
    <div class="love_text">生死契阔，与子成说。</div>
  </div>

  <script>
    var RENDERER = {
      INIT_CHERRY_BLOSSOM_COUNT: 30,
      MAX_ADDING_INTERVAL: 10,

      init: function () {
        this.setParameters();
        this.reconstructMethods();
        this.createCherries();
        this.render();
        if (
          navigator.userAgent.match(
            /(phone|pod|iPhone|iPod|ios|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone)/i
          )
        ) {
          var box = document.querySelectorAll('.box')[0];
          console.log(box, '移动端');
          box.style.marginTop = '65%';
        }
      },
      setParameters: function () {
        this.$container = $('#jsi-cherry-container');
        this.width = this.$container.width();
        this.height = this.$container.height();
        this.context = $('<canvas />')
          .attr({
            width: this.width,
            height: this.height
          })
          .appendTo(this.$container)
          .get(0)
          .getContext('2d');
        this.cherries = [];
        this.maxAddingInterval = Math.round(
          (this.MAX_ADDING_INTERVAL * 1000) / this.width
        );
        this.addingInterval = this.maxAddingInterval;
      },
      reconstructMethods: function () {
        this.render = this.render.bind(this);
      },
      createCherries: function () {
        for (
          var i = 0,
            length = Math.round(
              (this.INIT_CHERRY_BLOSSOM_COUNT * this.width) / 1000
            ); i < length; i++
        ) {
          this.cherries.push(new CHERRY_BLOSSOM(this, true));
        }
      },
      render: function () {
        requestAnimationFrame(this.render);
        this.context.clearRect(0, 0, this.width, this.height);

        this.cherries.sort(function (cherry1, cherry2) {
          return cherry1.z - cherry2.z;
        });
        for (var i = this.cherries.length - 1; i >= 0; i--) {
          if (!this.cherries[i].render(this.context)) {
            this.cherries.splice(i, 1);
          }
        }
        if (--this.addingInterval == 0) {
          this.addingInterval = this.maxAddingInterval;
          this.cherries.push(new CHERRY_BLOSSOM(this, false));
        }
      }
    };
    var CHERRY_BLOSSOM = function (renderer, isRandom) {
      this.renderer = renderer;
      this.init(isRandom);
    };
    CHERRY_BLOSSOM.prototype = {
      FOCUS_POSITION: 300,
      FAR_LIMIT: 600,
      MAX_RIPPLE_COUNT: 100,
      RIPPLE_RADIUS: 100,
      SURFACE_RATE: 0.5,
      SINK_OFFSET: 20,

      init: function (isRandom) {
        this.x = this.getRandomValue(
          -this.renderer.width,
          this.renderer.width
        );
        this.y = isRandom ?
          this.getRandomValue(0, this.renderer.height) :
          this.renderer.height * 1.5;
        this.z = this.getRandomValue(0, this.FAR_LIMIT);
        this.vx = this.getRandomValue(-2, 2);
        this.vy = -2;
        this.theta = this.getRandomValue(0, Math.PI * 2);
        this.phi = this.getRandomValue(0, Math.PI * 2);
        this.psi = 0;
        this.dpsi = this.getRandomValue(Math.PI / 600, Math.PI / 300);
        this.opacity = 0;
        this.endTheta = false;
        this.endPhi = false;
        this.rippleCount = 0;

        var axis = this.getAxis(),
          theta =
          this.theta +
          (Math.ceil(
              -(this.y + this.renderer.height * this.SURFACE_RATE) / this.vy
            ) *
            Math.PI) /
          500;
        theta %= Math.PI * 2;

        this.offsetY =
          40 * (theta <= Math.PI / 2 || theta >= (Math.PI * 3) / 2 ? -1 : 1);
        this.thresholdY =
          this.renderer.height / 2 +
          this.renderer.height * this.SURFACE_RATE * axis.rate;
        this.entityColor = this.renderer.context.createRadialGradient(
          0,
          40,
          0,
          0,
          40,
          80
        );
        this.entityColor.addColorStop(
          0,
          'hsl(330, 70%, ' + 50 * (0.3 + axis.rate) + '%)'
        );
        this.entityColor.addColorStop(
          0.05,
          'hsl(330, 40%,' + 55 * (0.3 + axis.rate) + '%)'
        );
        this.entityColor.addColorStop(
          1,
          'hsl(330, 20%, ' + 70 * (0.3 + axis.rate) + '%)'
        );
        this.shadowColor = this.renderer.context.createRadialGradient(
          0,
          40,
          0,
          0,
          40,
          80
        );
        this.shadowColor.addColorStop(
          0,
          'hsl(330, 40%, ' + 30 * (0.3 + axis.rate) + '%)'
        );
        this.shadowColor.addColorStop(
          0.05,
          'hsl(330, 40%,' + 30 * (0.3 + axis.rate) + '%)'
        );
        this.shadowColor.addColorStop(
          1,
          'hsl(330, 20%, ' + 40 * (0.3 + axis.rate) + '%)'
        );
      },
      getRandomValue: function (min, max) {
        return min + (max - min) * Math.random();
      },
      getAxis: function () {
        var rate = this.FOCUS_POSITION / (this.z + this.FOCUS_POSITION),
          x = this.renderer.width / 2 + this.x * rate,
          y = this.renderer.height / 2 - this.y * rate;
        return {
          rate: rate,
          x: x,
          y: y
        };
      },
      renderCherry: function (context, axis) {
        context.beginPath();
        context.moveTo(0, 40);
        context.bezierCurveTo(-60, 20, -10, -60, 0, -20);
        context.bezierCurveTo(10, -60, 60, 20, 0, 40);
        context.fill();

        for (var i = -4; i < 4; i++) {
          context.beginPath();
          context.moveTo(0, 40);
          context.quadraticCurveTo(i * 12, 10, i * 4, -24 + Math.abs(i) * 2);
          context.stroke();
        }
      },
      render: function (context) {
        var axis = this.getAxis();

        if (
          axis.y == this.thresholdY &&
          this.rippleCount < this.MAX_RIPPLE_COUNT
        ) {
          context.save();
          context.lineWidth = 2;
          context.strokeStyle =
            'hsla(0, 0%, 100%, ' +
            (this.MAX_RIPPLE_COUNT - this.rippleCount) /
            this.MAX_RIPPLE_COUNT +
            ')';
          context.translate(
            axis.x +
            this.offsetY * axis.rate * (this.theta <= Math.PI ? -1 : 1),
            axis.y
          );
          context.scale(1, 0.3);
          context.beginPath();
          context.arc(
            0,
            0,
            (this.rippleCount / this.MAX_RIPPLE_COUNT) *
            this.RIPPLE_RADIUS *
            axis.rate,
            0,
            Math.PI * 2,
            false
          );
          context.stroke();
          context.restore();
          this.rippleCount++;
        }
        if (axis.y < this.thresholdY || !this.endTheta || !this.endPhi) {
          if (this.y <= 0) {
            this.opacity = Math.min(this.opacity + 0.01, 1);
          }
          context.save();
          context.globalAlpha = this.opacity;
          context.fillStyle = this.shadowColor;
          context.strokeStyle =
            'hsl(330, 30%,' + 40 * (0.3 + axis.rate) + '%)';
          context.translate(
            axis.x,
            Math.max(axis.y, this.thresholdY + this.thresholdY - axis.y)
          );
          context.rotate(Math.PI - this.theta);
          context.scale(axis.rate * -Math.sin(this.phi), axis.rate);
          context.translate(0, this.offsetY);
          this.renderCherry(context, axis);
          context.restore();
        }
        context.save();
        context.fillStyle = this.entityColor;
        context.strokeStyle = 'hsl(330, 40%,' + 70 * (0.3 + axis.rate) + '%)';
        context.translate(
          axis.x,
          axis.y + Math.abs(this.SINK_OFFSET * Math.sin(this.psi) * axis.rate)
        );
        context.rotate(this.theta);
        context.scale(axis.rate * Math.sin(this.phi), axis.rate);
        context.translate(0, this.offsetY);
        this.renderCherry(context, axis);
        context.restore();

        if (this.y <= -this.renderer.height / 4) {
          if (!this.endTheta) {
            for (
              var theta = Math.PI / 2, end = (Math.PI * 3) / 2; theta <= end; theta += Math.PI
            ) {
              if (this.theta < theta && this.theta + Math.PI / 200 > theta) {
                this.theta = theta;
                this.endTheta = true;
                break;
              }
            }
          }
          if (!this.endPhi) {
            for (
              var phi = Math.PI / 8, end = (Math.PI * 7) / 8; phi <= end; phi += (Math.PI * 3) / 4
            ) {
              if (this.phi < phi && this.phi + Math.PI / 200 > phi) {
                this.phi = Math.PI / 8;
                this.endPhi = true;
                break;
              }
            }
          }
        }
        if (!this.endTheta) {
          if (axis.y == this.thresholdY) {
            this.theta +=
              (Math.PI / 200) *
              (this.theta < Math.PI / 2 ||
                (this.theta >= Math.PI && this.theta < (Math.PI * 3) / 2) ?
                1 :
                -1);
          } else {
            this.theta += Math.PI / 500;
          }
          this.theta %= Math.PI * 2;
        }
        if (this.endPhi) {
          if (this.rippleCount == this.MAX_RIPPLE_COUNT) {
            this.psi += this.dpsi;
            this.psi %= Math.PI * 2;
          }
        } else {
          this.phi += Math.PI / (axis.y == this.thresholdY ? 200 : 500);
          this.phi %= Math.PI;
        }
        if (this.y <= -this.renderer.height * this.SURFACE_RATE) {
          this.x += 2;
          this.y = -this.renderer.height * this.SURFACE_RATE;
        } else {
          this.x += this.vx;
          this.y += this.vy;
        }
        return (
          this.z > -this.FOCUS_POSITION &&
          this.z < this.FAR_LIMIT &&
          this.x < this.renderer.width * 1.5
        );
      }
    };
    $(function () {
      RENDERER.init();
    });
  </script>
</body>

</html>